package com.conversationboard.controller;

import java.io.IOException;
import java.security.Principal;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import com.conversationboard.authenticator.AuthenticationCookie;
import com.conversationboard.cache.readcounter.ReadCounterCache;
import com.conversationboard.config.Configuration;
import com.conversationboard.controller.ipaddress.IPAddress;
import com.conversationboard.formatter.ImageHider;
import com.conversationboard.logger.Logger;
import com.conversationboard.mobile.MobileDevice;
import com.conversationboard.model.Board;
import com.conversationboard.model.Boards;
import com.conversationboard.model.Message;
import com.conversationboard.model.Messages;
import com.conversationboard.model.NoSuchThreadException;
import com.conversationboard.model.PollItem;
import com.conversationboard.model.PollResult;
import com.conversationboard.model.PollResults;
import com.conversationboard.model.Role;
import com.conversationboard.model.Thread;
import com.conversationboard.model.User;
import com.conversationboard.site.Site;

@WebServlet(name = "ThreadSelectedControllerServlet", urlPatterns = "/ThreadSelectedControllerServlet")
public class ThreadSelectedControllerServlet extends HttpServlet {

	private static final long serialVersionUID = -4141049225097581680L;


	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}


	/* Security checks are already performed by the doGet */

	private void deliverAsXml(User user, HttpServletRequest request, HttpServletResponse response, boolean mobileDevice, String contextPath, String stylesheet, Board board, Thread thread,
			boolean showDeleted, int showLastMessages) throws SQLException, IOException, TransformerConfigurationException, TransformerException {

		StringBuilder buffer = new StringBuilder(thread.getMessageCount() * 50);

		buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");

		String stylesheetPath = Configuration.getInstance().getRoot() + "/Stylesheets/XSLT/" + stylesheet;

		if (stylesheet != null) {
			buffer.append("<?xml-stylesheet type=\"text/xsl\" href=\"" + stylesheetPath + "\"?>");
		}

		buffer.append("<Thread>\n");

		buffer.append("<URI>");
		buffer.append(contextPath);
		buffer.append("</URI>");

		/* Ensure dates formatted consistently */

		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

		/* Thread Level Information */

		buffer.append("\t<BoardId>" + thread.getBoardId() + "</BoardId>\n");
		buffer.append("\t<ThreadId>" + thread.getThreadId() + "</ThreadId>\n");
		buffer.append("\t<MessageCount>" + thread.getMessageCount() + "</MessageCount>\n");

		String title = thread.getTitle();
		String creatorId = thread.getCreatorId();

		String createdOnString = formatter.format(thread.getTimestamp());
		String lastUpdatedString = formatter.format(thread.getLastUpdated());

		buffer.append("\t<Title><![CDATA[" + title + "]]></Title>\n");
		buffer.append("\t<CreatorId><![CDATA[" + creatorId + "]]></CreatorId>\n");
		buffer.append("\t<CreatedOn>" + createdOnString + "</CreatedOn>\n");
		buffer.append("\t<Key>" + request.getSession().getId() + "</Key>\n");
		buffer.append("\t<LastUpdated>" + lastUpdatedString + "</LastUpdated>\n");
		buffer.append("\t<Deleted>" + thread.isDeleted() + "</Deleted>\n");
		buffer.append("\t<Worksafe>" + thread.isWorksafe() + "</Worksafe>\n");
		buffer.append("\t<Locked>" + thread.isLocked() + "</Locked>\n");
		buffer.append("\t<Poll>" + thread.isPoll() + "</Poll>\n");

		if (thread.isPoll()) {
			buffer.append("\t<PollClosingDate>" + thread.getPollClosingDate() + "</PollClosingDate>\n");
		}

		/* Poll Results */

		PollResults pollResults = thread.getPollResults();

		if (!(pollResults == null)) {

			List<PollResult> results = pollResults.getResults();

			buffer.append("\t<PollResults>\n");

			for (PollResult result : results) {
				buffer.append("\t\t<Result>\n");
				buffer.append("\t\t\t<Option><![CDATA[" + result.getOptionText() + "]]></Option\n>");
				buffer.append("\t\t\t<VoteCount>" + result.getVoteCount() + "</VoteCount>\n");
				buffer.append("\t\t</Result>\n");
			}

			buffer.append("\t</PollResults>\n");

		} else {

			if ((thread.isPoll()) && !thread.isPollClosed()) {

				List<PollItem> pollItems = thread.getPollItems();

				buffer.append("\t<PollOptions>\n");

				for (PollItem item : pollItems) {
					buffer.append("\t\t<Id>" + item.getOptionId() + "</Id>\n");
					buffer.append("\t\t<OptionText><![CDATA[" + item.getOptionText() + "]]></OptionText>\n");
				}

				buffer.append("\t</PollOptions>\n");
			}

		}

		/* Messages */

		buffer.append("\t<Messages>\n");

		Messages messages = thread.getMessages();

		int i = 0;

		for (Message message : messages) {

			/* If we've sent a 'showlastmessages' message, then only show the last x messages */

			if ((showLastMessages > 0) && (i++ < (messages.size() - showLastMessages))) {
				continue;
			}

			if (message.isDeleted() && (!showDeleted)) {
				continue;
			}

			String timestampString = formatter.format(message.getTimestamp());

			buffer.append("\t\t<Message>\n");
			buffer.append("\t\t\t<MessageId>" + message.getMessageId() + "</MessageId>\n");
			buffer.append("\t\t\t<Deleted>" + message.isDeleted() + "</Deleted>\n");
			buffer.append("\t\t\t<Timestamp>" + timestampString + "</Timestamp>\n");
			buffer.append("\t\t\t<DisplayName><![CDATA[" + message.getDisplayName() + "]]></DisplayName>\n");

			String body = message.getBody();

			/* If we're delivering to a mobile device, switch images for links to speed up loading */

			if (mobileDevice) {
				body = ImageHider.substituteImagesForLinks(message.getBody(), false, true, (!(user.getPreferences().isShowImages())));
			}

			buffer.append("\t\t\t<Body><![CDATA[" + body + "]]></Body>\n");
			buffer.append("\t\t</Message>\n");
		}

		buffer.append("\t</Messages>\n");
		buffer.append("</Thread>\n");

		String xmlData = buffer.toString();

		response.setContentType("text/xml");
		response.getWriter().append(xmlData);

	}


	/** Check to see if the user is accessing the site from a mobile device. If they are, set the transient
	 * property to reflect this.
	 * 
	 * @param request
	 * @param user */

	private void setMobileDeviceSettings(HttpServletRequest request) {

		request.setAttribute("mobiledevice", MobileDevice.isMobileDevice(request));

	}


	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		/* Use these purely so I can see them in the exception handler */

		int boardNumber = 0;
		int threadNumber = 0;

		try {

			Site.incrementPageHitCount();

			request.setCharacterEncoding("UTF-8");
			response.setCharacterEncoding("UTF-8");

			/* Collect parameters */

			String body = "";

			String id = request.getParameter("id");

			int boardId;
			int threadId;
			int bookmarkedMessageId;
			String deliverAsXml = request.getParameter("xml");

			/* Support two formats, either id=0-0-0 or the traditional boardid threadid and possibly
			 * bookmarkedmessageid */

			if (id != null) {
				String[] parts = id.split("-");
				boardId = Integer.parseInt(parts[0]);
				threadId = Integer.parseInt(parts[1]);
				bookmarkedMessageId = Integer.parseInt(parts[2]);

			} else {
				boardId = Integer.parseInt(request.getParameter("boardid"));
				threadId = Integer.parseInt(request.getParameter("threadid"));

				String bookmarkId = request.getParameter("bookmarkedmessageid");
				bookmarkedMessageId = 0;

				if (bookmarkId != null) {
					bookmarkedMessageId = Integer.parseInt(bookmarkId);
				}
			}

			boardNumber = boardId;
			threadNumber = threadId;

			/* Count number of reads of threads */

			ReadCounterCache.getInstance().incrementThreadCounter(boardId, threadId, request.getHeader("Referer"));

			/* Auto Login */

			Principal principal = request.getUserPrincipal();
			String loginId = null;

			if (principal == null) {
				if (AuthenticationCookie.getUserName(request) != null) {
					response.sendRedirect(Configuration.getInstance().getRoot() + "/LoginControllerServlet?autologin=true&referrer=ThreadSelectedControllerServlet%3Fboardid=" + boardId
							+ "%26threadid=" + threadId);
					return;
				}
			}

			if (principal != null) {
				loginId = principal.getName();
			}

			User user = User.get(loginId);

			this.setMobileDeviceSettings(request);

			if (user.getRole() != Role.Anonymous) {
				user.setIpAddress(IPAddress.getIPAddress(request));
				user.setLastAccessed(new Date());
			}

			/* Update the user's last logged in time and login count if necessary */

			user.updateLastLoggedIn(IPAddress.getIPAddress(request));

			Boolean bbCodeError = (request.getAttribute("bbCodeError") != null) ? (Boolean) request.getAttribute("bbCodeError") : false;
			String ignoreBbCodeErrors = request.getParameter("ignoreBbCodeErrors");

			if (bbCodeError && (ignoreBbCodeErrors == null || !ignoreBbCodeErrors.equals("on"))) {
				body = (String) request.getAttribute("body");
			}

			boolean xPosts = (request.getParameter("originalmessagecount")) != null;
			int originalMessageCount = 0;

			if (xPosts) {
				body = (String) request.getAttribute("body");
				originalMessageCount = Integer.parseInt(request.getParameter("originalmessagecount"));
			}

			String action = request.getParameter("action");
			Locale locale = request.getLocale();

			user.setLocale(locale);
			Board board = Boards.getBoard(boardId);

			if (board == null || user == null) {
				return;
			}

			/* Don't let a user view a thread on a private board unless they are a member of that board. */

			if (board.isPrivateBoard() && (!user.isMemberOfBoard(boardId))) {
				RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
				dispatcher.forward(request, response);
				return;
			}

			/* Construct the page forward */

			String requestString = "/Pages/thread.jsp";

			if (action != null && action.equals("showall")) {
				requestString += "?action=showall";
			}

			/* Get the thread and set the data in attributes to pass to the page */

			Thread thread = Thread.get(boardId, threadId);

			/* If you want the response as an XML document, respond to the client in this fashion and return */

			if ((deliverAsXml != null) && deliverAsXml.equalsIgnoreCase("true")) {

				String showDeleted = request.getParameter("showdeleted");
				int showLastMessages = 0;

				if (request.getParameter("showlastmessages") != null) {
					showLastMessages = Integer.parseInt(request.getParameter("showlastmessages"));
				}

				String stylesheet = request.getParameter("stylesheet");

				boolean mobileDevice = MobileDevice.isMobileDevice(request);
				this.deliverAsXml(user, request, response, mobileDevice, request.getContextPath(), stylesheet, board, thread, (showDeleted != null), showLastMessages);

				return;
			}

			request.setAttribute("board", board);
			request.setAttribute("user", user);
			request.setAttribute("thread", thread);

			if (ignoreBbCodeErrors != null && ignoreBbCodeErrors.equals("on")) {
				request.setAttribute("ignoreBbCodeErrors", true);
			} else if (bbCodeError) {
				request.setAttribute("body", body);
			}

			request.setAttribute("bookmarkedmessageid", bookmarkedMessageId);

			if (xPosts) {
				request.setAttribute("originalmessagecount", originalMessageCount);
				request.setAttribute("body", body);
			}

			RequestDispatcher dispatcher = request.getRequestDispatcher(requestString);
			dispatcher.forward(request, response);

		} catch (NoSuchThreadException e) {
			RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
			request.setAttribute("message", "This thread does not appear to exist. If you had it bookmarked, perhaps the thread has been moved to another board.");
			dispatcher.forward(request, response);
		} catch (SQLException e) {
			throw new ServletException(e);
		} catch (ServletException e) {
			throw new ServletException(e);
		} catch (IOException e) {
			throw new ServletException(e);
		} catch (NumberFormatException e) {
			/* Do nothing */
		} catch (TransformerConfigurationException e) {
			throw new ServletException(e);
		} catch (TransformerException e) {
			Logger.log(new Date(), "Transformer exception... Board: " + boardNumber + " Thread: " + threadNumber, User.getAnonymousUser());
			throw new ServletException(e);
		}

	}

}
